<?php
	/**
	 * MusicNet Alignment Tool
	 *
	 * Copyright (c) 2010 Joe Lambert
	 * 
	 * Permission is hereby granted, free of charge, to any person obtaining
	 * a copy of this software and associated documentation files (the
	 * "Software"), to deal in the Software without restriction, including
	 * without limitation the rights to use, copy, modify, merge, publish,
	 * distribute, sublicense, and/or sell copies of the Software, and to
	 * permit persons to whom the Software is furnished to do so, subject to
	 * the following conditions:
	 * 
	 * The above copyright notice and this permission notice shall be
	 * included in all copies or substantial portions of the Software.
	 * 
	 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
	 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
	 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
	 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
	 *  
	 * @author Joe Lambert
	 * @version 1.0
	 * @copyright Joe Lambert, 23 August, 2010
	 **/
	
	require_once("../shared/database.php");
	require_once("../shared/musicnet.php");
	
	class Ajax
	{
		// Defaults
		private $status = 200;
		private $message = "Success";
		private $data = array();
		
		/**
		 * Constructor
		 *
		 * @return void
		 * @author Joe Lambert
		 **/
		public function __construct()
		{
			$db = Database::instance();
			$db->query("SET NAMES 'utf8'");
		}
		
		public function ungrouped($type="ungrouped")
		{
			$query = Database::instance()->from('items')->where('match_id', 'NULL');
			
			if(isset($_GET['a2z']))
				$query->where('a2z', $_GET['a2z']);
				
			$query->where('trashed', $type=="trashed"?'1':'0');
				
			$limit = 1000;	
			if(isset($_GET['limit']))
				$limit = $_GET['limit'];
				
			//$query->limit($limit);
			$query->orderby('hash');
			
			$result = $query->get();
			
			while($row = mysql_fetch_assoc($result))
			{
				$this->data[] = array(
						'id'	=> $row['id'],
						'label'	=> $row['label'],
					);
			}
		}
		
		public function grouped($type="verified")
		{
			if(!in_array($type, array('verified', 'unverified')))
				$type = 'verified';
				
			$verified = ($type == 'verified' ? '1' : '0');
			
			$query = Database::instance()->select(array('match_id'))->from('items');
			
			if($verified)
				$query->where(array('verified_match' => '1'));
			else
				$query->where(array('verified_match' => '0', 'match_id !=' => ''));
			
			if(isset($_GET['a2z']))
				$query->where('a2z', $_GET['a2z']);
			
			$query->where('trashed', '0');
			
			$result = $query->get();
			
			$ids = array();
			while($row = mysql_fetch_assoc($result))
				$ids[] = $row['match_id'];
			
			$ids = array_unique($ids);
			
			if(count($ids) == 0)
			{
				return;
			}
			
			$result = Database::instance()->select(array(
					"matches.id" => "match_id",
					"items.id" => "item_id",
					"hash",
					"canonical_label",
					"label",
					"verified_match"
				))->from('matches')->leftjoin('items', array('items.match_id', 'matches.id'))->in('matches.id', $ids)->orderby('items.hash')->get();
			
			$m_id = "";
			$current_set = array();
			$previous_groups = array();
			
			// This flag is used to discard partially unverified groups from the verified list (this may occur when adding new/updated data and re-running the alignment script)
			$contains_unverified = false;
			
			while($row = mysql_fetch_assoc($result))
			{
				if($m_id != $row['match_id'])
				{
					if(!empty($current_set) && ($type == 'unverified' || !$contains_unverified))
					{
						if(empty($previous_groups[$current_set['id']]))
						{
							$this->data[] = $current_set;
							$previous_groups[$current_set['id']] = count($this->data)-1;
						}
						else
						{
							$group_index = $previous_groups[$current_set['id']];
							$this->data[$group_index]['items'] = array_merge($this->data[$group_index]['items'], $current_set['items']);
						}
					}
					
					$contains_unverified = false;	
					$m_id = $row['match_id'];
					$current_set = array(
									'id' 	=> $row['match_id'],
									'label' => $row['canonical_label'] ? $row['canonical_label'] : $row['label'],
									'items'	=> array(),
								);
				}
				
				if($row['verified_match']==0)
					$contains_unverified = true;
				
				$current_set['items'][] = array(
						'id'	=> $row['item_id'],
						'label'	=> $row['label'],
						'verified' => $row['verified_match']==1,
					);
			}
			
			if(!empty($current_set))
				$this->data[] = $current_set;
		}
		
		public function metadata($id=null)
		{			
			if(isset($id))
			{
				$result = Database::instance()->from('items')->leftjoin('metadata', array('items.id', 'metadata.item_id'))->where('id', $id)->get();
				
				if(mysql_num_rows($result) == 0)
				{
					// Perhaps this is the ID for a group?
					$result = Database::instance()->from('items')->leftjoin('metadata', array('items.id', 'metadata.item_id'))->where('match_id', $id)->get();
				}
			}
			else
			{
				if(isset($_GET['id']))
					$ids = $_GET['id'];
				else
					$ids = null;
				
				if(is_array($ids))
				{
					$result = Database::instance()->from('items')->leftjoin('metadata', array('items.id', 'metadata.item_id'))->in('id', $ids)->orderby('items.hash')->get();
					
					if(mysql_num_rows($result) == 0)
					{
						// Perhaps this is the ID for a group?
						$result = Database::instance()->from('items')->leftjoin('metadata', array('items.id', 'metadata.item_id'))->in('match_id', $ids)->orderby('items.hash')->get();	
					}
				}
				else
				{
					$this->status = 400;
					$this->message = "No ID provided";
					return;
				}	
			}
			
			// Now we have a set of results we need to create an array of objects
			
			$id = "";
			$item = null;

			while($row = mysql_fetch_assoc($result))
			{
				if($id != $row['id'])
				{
					$id = $row['id'];
					
					if(isset($item))
						$this->data[] = $item;
					
					$item = new stdClass();
					$item->metadata = new stdClass();
				}
				
				$item->label = $row['label'];
				$item->id = $row['id'];
				$item->metadata->{$row['key']} = $row['value'];
			}
			
			$this->data[] = $item; // Make sure to catch the last created item;
			
			// Render the array of objects using a view
			
			ob_start();
			
			include_once("views/musicnet.php");
			
			$html = ob_get_contents();
			ob_end_clean();
			
			echo $html;die;
		}
		
		private function _group_create($ids)
		{
			$match_id = MusicNet::createGroupID($ids);
			
			$match = array();
			$match['match_id'] = $match_id;
			
			// Setup succesful return data
			$result = Database::instance()->from('items')->in('id', $ids)->get();
			
			$match['items'] = array();
			while($row = mysql_fetch_assoc($result))
				$match['items'][] = $row;
			
			// Update the database
			$query = "INSERT INTO matches (id, canonical_label) VALUES ('".mysql_real_escape_string($match_id)."', '".mysql_real_escape_string($match['items'][0]['label'])."')";
			
			Database::instance()->query("BEGIN");
			
			$result = Database::instance()->query($query);
			
			if(!$result)
			{
				Database::instance()->query("ROLLBACK");
				$this->status = 500;
				$this->message = "Could not update the database";
				$this->data = array($query);
				return false;
			}
			else
			{
				foreach($match['items'] as $item)
				{
					$query = "UPDATE items SET match_id = '".mysql_real_escape_string($match_id)."', verified_match=1, trashed=0 WHERE id = '".mysql_real_escape_string($item['id'])."'";
					$result = Database::instance()->query($query);
					
					if(!$result)
					{
						Database::instance()->query("ROLLBACK");
						$this->status = 500;
						$this->message = "Could not update the database";
						$this->data = array($query);
						return false;
					}
				}
			}
			
			Database::instance()->query("COMMIT");
			
			return $match;
		}
		
		public function group_create()
		{
			if(isset($_GET['id']))
				$ids = $_GET['id'];
			else
				$ids = null;
			
			if(is_array($ids))
			{
				$this->data[] = $this->_group_create($ids);
			}
			else
			{
				$this->status = 400;
				$this->message = "No ID's provided";
				return;
			}
		}
		
		public function group_createsingles()
		{
			if(isset($_GET['id']))
				$ids = $_GET['id'];
			else
				$ids = null;
			
			if(is_array($ids))
			{
				foreach($ids as $id)
				{
					$id = array($id);
					$this->data[] = $this->_group_create($id);
				}
			}
			else
			{
				$this->status = 400;
				$this->message = "No ID's provided";
				return;
			}
		}
		
		public function group_add()
		{
			if(isset($_GET['id']))
				$ids = $_GET['id'];
			else
				$ids = null;
				
			if(isset($_GET['gid']))
				$gid = $_GET['gid'];
			else
				$gid = null;
			
			if(is_array($ids))
			{
				if(isset($gid))
				{
					Database::instance()->query("BEGIN");
					
					foreach($ids as $id)
					{
						$query = "UPDATE items SET match_id = '".mysql_real_escape_string($gid)."', verified_match=1, trashed=0 WHERE id = '".mysql_real_escape_string($id)."'";
						$result = Database::instance()->query($query);
					
						if(!$result)
						{
							Database::instance()->query("ROLLBACK");
							$this->status = 500;
							$this->message = "Could not update the database";
							$this->data = array($query);
							return;
						}	
					}
					
					Database::instance()->query("COMMIT");
					
					// Setup succesful return data
					$result = Database::instance()->from('items')->in('id', $ids)->get();

					$this->data['items'] = array();
					while($row = mysql_fetch_assoc($result))
						$this->data['items'][] = $row;
						
					$this->data['match_id'] = $gid;
				}
				else
				{
					$this->status = 400;
					$this->message = "No Group ID provided";
					return;
				}
			}
			else
			{
				$this->status = 400;
				$this->message = "No ID's provided";
				return;
			}
		}
		
		public function group_verify()
		{				
			if(isset($_GET['gid']))
				$gids = $_GET['gid'];
			else
				$gids = null;
			
			if(is_array($gids))
			{
				// Update the DB
				$ids = "";
				$i = 0;
				foreach($gids as $in)
				{
					if($i++ > 0)
						$ids .= ", ";
						
					if(!is_numeric($in))
						$ids .= "'".mysql_real_escape_string($in)."'";
					else
						$ids .= mysql_real_escape_string($in);
				}
				
				$result = Database::instance()->query("UPDATE items SET verified_match=1 WHERE match_id IN ($ids)");
				
				if(!$result)
				{
					$this->status = 500;
					$this->message = "Could not update the database";
					$this->data = array($query);
					return;
				}
				
				// Setup succesful return data
				$result = Database::instance()->from('matches')->in('id', $gids)->get();

				$this->data['groups'] = array();
				while($row = mysql_fetch_assoc($result))
					$this->data['groups'][] = $row;
			}
			else
			{
				$this->status = 400;
				$this->message = "No Group ID's provided";
				return;
			}
		}
		
		public function group_merge()
		{
			if(isset($_GET['gid']))
				$gids = $_GET['gid'];
			else
				$gids = null;
			
			if(is_array($gids))
			{
				$result = Database::instance()->from('items')->in('match_id', $gids)->get();
				
				$this->data['old_groups'] = array();
				
				// Is the new merged group verified or not?
				$verified = true;
				$label = '';
				$group_ids = array();
				while($row = mysql_fetch_assoc($result))
				{
					$verified = $row['verified_match'];
					$label = $row['label'];
					$group_ids[] = $row['match_id'];
					
					$this->data['old_groups'][] = $row['match_id'];
				}
				$group_ids = array_unique($group_ids);

				// Create a new uniqu ID for this merged group
				$new_group = "musicnet".md5(uniqid());
				$this->data['new_group'] = $new_group;
				
				// Create a new row in matches table
				$query = "INSERT INTO matches (`id`, `canonical_label`) VALUES('".mysql_real_escape_string($new_group)."', '".mysql_real_escape_string($label)."')";
				//echo $query."<br/>";
				mysql_query($query);
				
				// Remove previous rows
				$query = "DELETE FROM matches WHERE id IN ('".implode("','", $group_ids)."')";
				//echo $query."<br/>";
				mysql_query($query);
				
				// Change the rows in the items table to use the new group
				$query = "UPDATE items SET verified_match = ".mysql_real_escape_string($verified).", match_id = '".mysql_real_escape_string($new_group)."' WHERE match_id IN ('".implode("','", $group_ids)."')";
				//echo $query."<br/>";die;
				mysql_query($query);
			}
			else
			{
				$this->status = 400;
				$this->message = "No Group ID's provided";
				return;
			}
		}
		
		public function group_remove()
		{				
			if(isset($_GET['gid']))
				$gids = $_GET['gid'];
			else
				$gids = null;
			
			if(is_array($gids))
			{
				// Setup succesful return data
				$result = Database::instance()->from('matches')->in('id', $gids)->get();

				$this->data['groups'] = array();
				while($row = mysql_fetch_assoc($result))
					$this->data['groups'][] = $row;
					
				// Update the DB
				$result = Database::instance()->query('BEGIN');
				
				$ids = "";
				$i = 0;
				foreach($gids as $in)
				{
					if($i++ > 0)
						$ids .= ", ";
						
					if(!is_numeric($in))
						$ids .= "'".mysql_real_escape_string($in)."'";
					else
						$ids .= mysql_real_escape_string($in);
				}
				
				$result1 = Database::instance()->query("UPDATE items SET match_id=NULL WHERE match_id IN ($ids)");
				
				$result2 = Database::instance()->query("DELETE FROM matches WHERE id IN ($ids)");
				
				if(!$result1 && !$result2)
				{
					Database::instance()->query("ROLLBACK");
					$this->status = 500;
					$this->message = "Could not update the database";
					$this->data = array($query);
					return;
				}
				
				$result = Database::instance()->query('COMMIT');
			}
			else
			{
				$this->status = 400;
				$this->message = "No Group ID's provided";
				return;
			}
		}
		
		public function group_removeitem()
		{
			if(isset($_GET['id']))
				$id = $_GET['id'];
			else
				$id = null;
				
			if(isset($_GET['gid']))
				$gid = $_GET['gid'];
			else
				$gid = null;
				
			if(isset($id))
			{
				if(isset($gid))
				{
					$query = "UPDATE items SET match_id=NULL WHERE id = '".mysql_real_escape_string($id)."'";
					$result = Database::instance()->query($query);
				
					if(!$result)
					{
						$this->status = 500;
						$this->message = "Could not update the database";
						$this->data = array($query);
						return;
					}
				
					// If that was the last item in a group then we should delete the match as well
					$result = Database::instance()->from("items")->where('match_id', $gid)->get();
				
					if(!mysql_num_rows($result))
					{
						$query = "DELETE FROM matches WHERE id = '".mysql_real_escape_string($gid)."'";
						$result = Database::instance()->query($query);
					
						if(!$result)
						{
							$this->status = 500;
							$this->message = "Could not update the database";
							$this->data = array($query);
							return;
						}
					}	
				}
				else
				{
					$this->status = 400;
					$this->message = "No Group ID provided";
					return;
				}
			}
			else
			{
				$this->status = 400;
				$this->message = "No Item ID provided";
				return;
			}
			
		}
		
		public function trash_item()
		{
			if(isset($_GET['id']))
				$ids = $_GET['id'];
			else
				$ids = null;
				
			if(isset($ids))
			{
				// Get a list of all matches these rows affect
				$result = Database::instance()->select('match_id')->from('items')->in('id', $ids)->get();
				
				$match_ids = array();
				
				while($row = mysql_fetch_assoc($result))
				{
					if($row['match_id'])
						$match_ids[] = $row['match_id'];
				}
				
				$safe_matches_ids = "'".implode("', '", $match_ids)."'";

				// Update the item rows
				$safe_ids = "'".implode("', '", $ids)."'";

				$query = "UPDATE items SET match_id = NULL, trashed = 1 WHERE id IN ($safe_ids)";
				$result = Database::instance()->query($query);

				// Find any matches that no longer have items
				$query = "SELECT matches.id AS match_id, count(matches.id) AS 'count' FROM matches, items WHERE items.match_id = matches.id AND matches.id IN ($safe_matches_ids) GROUP BY matches.id";
				$result = Database::instance()->query($query);
				
				$populated_matches = array();
				
				while($row = mysql_fetch_assoc($result))
				{
					$populated_matches[] = $row['match_id'];
				}
				
				$safe_matches_ids = "'".implode("', '", array_diff($match_ids, $populated_matches))."'";
				
				// Remove matches with no items
				$query = "DELETE FROM matches WHERE id IN ($safe_matches_ids)";
				
				$result = Database::instance()->query($query);
			}
			else
			{
				$this->status = 400;
				$this->message = "No Item ID provided";
				return;
			}
		}
		
		public function untrash_item()
		{
			if(isset($_GET['id']))
				$ids = $_GET['id'];
			else
				$ids = null;
				
			if(isset($ids))
			{
				// Update the item rows
				$safe_ids = "'".implode("', '", $ids)."'";

				$query = "UPDATE items SET trashed = 0 WHERE id IN ($safe_ids)";
				$result = Database::instance()->query($query);
			}
			else
			{
				$this->status = 400;
				$this->message = "No Item ID provided";
				return;
			}
		}
		
		/**
		 * Output the response to the client request
		 *
		 * @return void
		 * @author Joe Lambert
		 **/
		public function output()
		{
			if($this->status == 404)
				header('HTTP/1.0 404 Not Found');
			
			$output = array(
					"status"	=> $this->status,
					"message"	=> $this->message,
					"data"		=> $this->data,
				);
				
			header("Content-Type: application/json");	
			echo json_encode($output);
		}
		
		/**
		 * Catch un-documented method requests
		 * @param string $method
		 * @param mixed $args 
		 *  
		 * @return void
		 * @author Joe Lambert
		 **/
		public function __call($method, $args)
		{
			$this->status = 404;
			$this->message = "Unknown method: '$method'";
		}
	}
?>